import 'dart:async';
import 'dart:typed_data';

import 'package:bluetooth_spp/bluetooth_spp.dart';
import 'package:sxw_bluetooth_provider/src/device/bluetooth_device.dart';
import 'package:sxw_bluetooth_provider/src/config/config.dart';
import 'package:sxw_bluetooth_provider/src/utils/message_merger.dart';
import 'package:sxw_bluetooth_provider/src/utils/string_utils.dart';

import 'connection.dart';

class SppDelegate implements ConnectionHelper<SxwSppDevice, SppConfig> {
  final ConnectionDelegate delegate;

  Spp get spp => Spp();

  SppDelegate(this.delegate);

  @override
  void connect(SppConfig config, Completer<SxwBluetoothDevice> completer) {
    var sppConnection = _SppConnection(this, config, completer);
    sppConnection.connect();
  }

  @override
  List<SxwSppDevice> get connectedDeviceList => _devices.toList();

  List<SxwSppDevice> _devices = [];

  void onDeviceChange(SxwSppDevice device, bool connectState) {
    if (connectState) {
      _devices.add(device);
    } else {
      _devices.removeWhere((test) => test.id == device.id);
    }
    delegate.onConnectedDeviceChange();
  }

  void onGetData(BluetoothSppDevice device, Uint8List data) {}
}

class _SppConnection {
  final Completer<SxwBluetoothDevice> completer;

  SppDelegate delegate;

  SppConfig config;

  BluetoothSppConnection connection;

  BluetoothSppDevice device;

  SxwSppDevice sxwSppDevice;

  Spp get spp => Spp();

  _SppConnection(this.delegate, this.config, this.completer);

  StreamSubscription bondStateSub;
  StreamSubscription dataSub;
  StreamSubscription connectSub;
  StreamSubscription deviceSub;

  Future<void> connect() async {
    final device = spp.devices().firstWhere(
          (test) => StringUtils.equalsIgnoreCase(test.name, config.name),
          orElse: () => null,
        );

    if (device != null) {
      _connectDevice(device);
      return;
    }

    Future.delayed(config.scanTimeout, () {
      spp.stopScan();
    });
    // 扫描
    deviceSub = spp.foundDeviceStream.listen((device) {
      sppChange(device);
    });
    spp.scan();
  }

  void sppChange(BluetoothSppDevice device) {
    if (StringUtils.equalsIgnoreCase(device.name, config.name)) {
      spp.stopScan();
      _connectDevice(device);
    }
  }

  bool connected = false;
  StreamSubscription bondStreamSub;
  void _connectDevice(BluetoothSppDevice device) async {
    if (connected) {
      return;
    }
    connected = true;

    this.device = device;
    final connection = await spp.connect(device);
    this.connection = connection;

    connectSub = connection.connectStream.listen(onConnectStateChanged);

    final merger = MessageMerger(notifier: (data) {
      sxwSppDevice?.onReceive(data);
    });

    dataSub = connection.dataStream.listen((data) {
      merger.addData(data);
    });

    if (device.bondState != BondState.bonded) {
      bondStreamSub = connection.bondStateStream.listen(bondStateChange);
      await connection.bond(config.pin);
      return;
    }

    connection.connect();
  }

  bool alreadyRequestBond = false;

  void bondStateChange(BondState state) {
    if (connection.bondState == BondState.none) {
      if (alreadyRequestBond) {
        bondStreamSub.cancel();
        // 到这里可以认为连接失败, 因为无法绑定设备
        completer.complete(null);
        return;
      }
      connection.bond(config.pin);
      alreadyRequestBond = true;
      return;
    } else if (connection.bondState == BondState.bonding) {
      print("绑定设备中");
      return;
    } else if (connection.bondState == BondState.bonded) {
      connection.connect();
    }
  }

  void onConnectStateChanged(bool connectState) {
    if (!connectState) {
      dispose();
    } else {
      sxwSppDevice = SxwSppDevice(
        config: config,
        device: device,
      );
      completer.complete(sxwSppDevice);
      sxwSppDevice.canUse = true;
    }
    delegate.onDeviceChange(sxwSppDevice, connectState);
  }

  void dispose() {
    deviceSub?.cancel();
    bondStateSub?.cancel();
    connectSub?.cancel();
    dataSub?.cancel();
  }
}
